/**
 * 实现“观察者模式”：管理员查看其他员工的聊天消息，且可代替其发送消息
 */
import { MemStream } from '@gnlab/utils/rx'
import { parseQuery, stringifyQuery } from '@gnlab/utils/url'
import { firstValueFrom, BehaviorSubject, filter, map, combineLatest } from 'rxjs'
import StorageManager from '@/lib/storage'
import { getAccounts } from '@/services/features/accounts'
import { type Account, type BaseAccount } from './definition'
import { account$ } from './info'

/**
 * “观察者模式”状态
 */
export enum ObservingStatus {
  Off = 'Off', // 未开启
  ToBeConfirm = 'ToBeConfirm', // 信息待验证
  Observing = 'Observing', // 观察中
  Failed = 'Failed', // 进入观察失败
}

export type ObservingInfo =
  | { status: ObservingStatus.Off }
  | { status: ObservingStatus.ToBeConfirm; accountIds: string[] }
  | { status: ObservingStatus.Observing; accounts: BaseAccount[]; readonly: boolean }
  | { status: ObservingStatus.Failed; message: string }

/**
 * -------------------------------
 * 辅助信息
 * -------------------------------
 */

class Storage extends StorageManager<{ readonly: boolean }> {
  constructor() {
    super({
      topic: `observing`,
      defaults: { readonly: true },
    })
  }
}
const storage = new Storage()

const DEFAULT_PREFIX = 'apps-'

/**
 * -------------------------------
 * 外部传入参数整理
 * -------------------------------
 */

// 通过 URL 指定被观察的账号 ID，多个账号用 ',' 分隔
// 指定时可省略 apps- 前缀
const accountIdsInQuery = (parseQuery(location.search).observing ?? '')
  .split(',')
  .filter(v => v.length)
  .map(rawId => (rawId.includes('-') ? rawId : DEFAULT_PREFIX + rawId))

/**
 * -------------------------------
 * 功能实现
 * -------------------------------
 */

// “观察者模式”信息
const info$$ = new BehaviorSubject<ObservingInfo>(
  accountIdsInQuery.length
    ? { status: ObservingStatus.ToBeConfirm, accountIds: accountIdsInQuery }
    : { status: ObservingStatus.Off },
)
export const info$ = new MemStream(info$$.value, info$$.asObservable())

function syncInfo() {
  combineLatest([
    account$.pipe(filter((account): account is Account => !!account && account.isAdmin)),
    info$.pipe(filter(info => info.status === ObservingStatus.ToBeConfirm)),
  ]).subscribe(async ([account]) => {
    const res = await getAccounts(account, account.token)
    if (res.success) {
      const accounts = accountIdsInQuery.map(accountId => res.data.find(v => v.id === accountId))

      if (accounts.includes(undefined)) {
        info$$.next({
          status: ObservingStatus.Failed,
          message: '部分账号不存在，进入观察者模式失败',
        })
      } else {
        info$$.next({
          status: ObservingStatus.Observing,
          accounts: accounts as Account[],
          readonly: storage.data.readonly,
        })
      }
    } else {
      info$$.next({
        status: ObservingStatus.Failed,
        message: '账号信息获取失败，无法进入观察者模式：' + res.error,
      })
    }
  })
}
syncInfo()

/**
 * 是否处在观察者模式
 */
export const in$ = new MemStream(
  false,
  info$.pipe(map(info => info.status === ObservingStatus.Observing)),
)

/**
 * 是否处在观察者模式，且在只读状态
 */
export const readonly$ = new MemStream(
  false,
  info$.pipe(map(info => info.status === ObservingStatus.Observing && info.readonly)),
)

/**
 * 被观察的账号信息
 */
export const accounts$ = new MemStream(
  null,
  info$$.pipe(map(info => (info.status === ObservingStatus.Observing ? info.accounts : null))),
)

/**
 * 被观察的账号的 ID 列表
 */
export const accountIds = firstValueFrom(
  info$$.pipe(
    filter(info => info.status !== ObservingStatus.ToBeConfirm),
    map(info =>
      info.status === ObservingStatus.Observing ? info.accounts.map(acc => acc.id) : null,
    ),
  ),
)

/**
 * 切换观察者模式只读状态
 */
export function switchReadonly() {
  const info = info$$.value
  if (info.status === ObservingStatus.Observing) {
    info$$.next({ ...info, readonly: !info.readonly })
    storage.save({ ...storage.data, readonly: !info.readonly })
  }
}

/**
 * 离开观察者模式
 */
export function leave() {
  const query = parseQuery(location.search)
  delete query.observing
  const search = stringifyQuery(query)
  location.search = search ? '?' + search : ''
}

/**
 * 进入观察者模式
 */
export function go(accountIds: string[]) {
  const formatted = accountIds.map(id => id.replace(DEFAULT_PREFIX, '')).join(',')
  window.open('?observing=' + formatted)
}
